Skip to content

🔥 feat(nextjs): Bound Optimization components & Isomorphic Optimization Provider#355

Merged
Charles Hudson (phobetron) merged 1 commit into
mainfrom
NT-3560_nextjs-ssr-hybrid-metaframework-fix
Jul 6, 2026
Merged

🔥 feat(nextjs): Bound Optimization components & Isomorphic Optimization Provider#355
Charles Hudson (phobetron) merged 1 commit into
mainfrom
NT-3560_nextjs-ssr-hybrid-metaframework-fix

Conversation

@phobetron

@phobetron Charles Hudson (phobetron) commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR introduces router-specific bound Next.js SDK integration APIs for @contentful/optimization-nextjs:

  • App Router: createNextjsAppRouterOptimization() from @contentful/optimization-nextjs/app-router
  • Pages Router client components: createNextjsPagesRouterOptimization() from @contentful/optimization-nextjs/pages-router
  • Pages Router server handoff: createNextjsPagesRouterOptimization() from @contentful/optimization-nextjs/pages-router/server

Consumers can now bind Optimization SDK configuration once in an app-local module and use the returned OptimizationRoot, OptimizationProvider, OptimizedEntry, route trackers, App Router proxy, or Pages Router getServerSideOptimizationProps() across server and client rendering boundaries.

The change also brings in the isomorphic SSR provider/runtime foundation needed for those APIs: React Web can render personalized content during SSR from snapshot state, then hydrate into the live browser SDK without app code branching on typeof window.

It also extends the React Web OptimizedEntry render-prop contract with OptimizedEntryRenderContext and getMergeTagValue, exposes runtime/presentation helpers used by SSR handoff and tracking, replaces the older Next.js SSR/Hybrid reference apps with App Router and Pages Router implementations, and refreshes docs, package exports, CI, scripts, unit tests, and browser E2E coverage around the new router-specific integration path.

Credit

Credit to Tim's feat/ssr-isomorphic-provider branch and PR #354 for the isomorphic SSR foundation adopted here, including OptimizationRuntime, SnapshotRuntime, runtime package subpaths, server-renderable OptimizationProvider, serverOptimizationState first-paint handoff, SSR variant-rendering fixes, injected-SDK first-paint fixes, and JavaScript-disabled SSR validation.

Example usage

App Router

// lib/optimization.ts
import { createNextjsAppRouterOptimization } from '@contentful/optimization-nextjs/app-router'
import { getAppConsent } from './consent'

export const { proxy, NextAppAutoPageTracker, OptimizationRoot, OptimizedEntry } =
  createNextjsAppRouterOptimization({
    clientId: process.env.NEXT_PUBLIC_CONTENTFUL_OPTIMIZATION_CLIENT_ID ?? '',
    environment: process.env.NEXT_PUBLIC_CONTENTFUL_OPTIMIZATION_ENVIRONMENT ?? 'main',
    locale: 'en-US',
    defaults: { consent: false, persistenceConsent: false },
    server: {
      enabled: true,
      consent: ({ cookies }) =>
        getAppConsent(cookies) ? { events: true, persistence: true } : false,
    },
    trackEntryInteraction: { views: true, clicks: true, hovers: true },
  })
// proxy.ts
export { proxy } from '@/lib/optimization'

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico|api).*)'],
}

Pages Router

// lib/optimization.ts
import { createNextjsPagesRouterOptimization } from '@contentful/optimization-nextjs/pages-router'

export const { NextPagesAutoPageTracker, OptimizationRoot, OptimizedEntry } =
  createNextjsPagesRouterOptimization({
    clientId: process.env.NEXT_PUBLIC_CONTENTFUL_OPTIMIZATION_CLIENT_ID ?? '',
    environment: process.env.NEXT_PUBLIC_CONTENTFUL_OPTIMIZATION_ENVIRONMENT ?? 'main',
    locale: 'en-US',
    defaults: { consent: false, persistenceConsent: false },
    trackEntryInteraction: { views: true, clicks: true, hovers: true },
  })

Architectural and design changes

  • Adds router-specific public Next.js SDK entrypoints for /app-router, /pages-router, and /pages-router/server.
  • Uses conditional package exports so /app-router resolves to the automatic server implementation in React Server Component contexts and the client implementation in browser/client contexts.
  • Keeps the package root and /client as router-neutral React Web client surfaces.
  • Keeps /server, /esr, /request-handler, /tracking-attributes, and /api-schemas as lower-level escape hatches for custom flows.
  • Moves App Router request work into the bound proxy/server implementation: request consent, cookies, headers, server Optimization data, entry resolution, merge-tag profile reads, tracking attributes, anonymous-ID persistence, and serverOptimizationState handoff.
  • Adds a Pages Router server helper that builds request context from getServerSideProps, calls the request-bound SDK, writes or clears ctfl-opt-aid, returns serializable props.contentfulOptimization, and avoids duplicate initial page events when the server already emitted one.
  • Adopts the isomorphic runtime model from ✨ feat(react-web): Isomorphic OptimizationProvider for SSR (NT-3560) #354: SSR uses a read-only snapshot runtime, while hydrated browser rendering upgrades to the live SDK.
  • Removes NextjsOptimizationState in favor of serverOptimizationState handoff through root/provider components, automatic App Router handoff, or Pages Router pageProps handoff.
  • Extends React Web OptimizedEntry render props from (resolvedEntry) to (resolvedEntry, context), where context.getMergeTagValue is available to rich text renderers.
  • Adds runtime subpaths for core/web packages and public presentation helpers for optimized-entry tracking/root runtime behavior.
  • Updates package export maps, dual declaration generation, bundle-size budgets, CI path filters, root scripts, and implementation names around the new router-specific outputs.
  • Adds App Router, Pages Router, request-handler, runtime, React Web, Web SDK, and browser E2E coverage for the new contracts.

Isomorphic SSR foundation

  • Adds OptimizationRuntime, SnapshotRuntime, createSnapshotRuntime, and runtime package subpaths for core/web.
  • Makes OptimizationProvider / OptimizationRoot render during SSR from serverOptimizationState.
  • Allows useOptimization() and OptimizedEntry to resolve personalized content during SSR from snapshot state, then switch to the live SDK after hydration.
  • Fixes SSR first-paint variant rendering by seeding snapshot/current optimization state before client effects run.
  • Preserves injected-SDK first paint when serverOptimizationState is provided.
  • Aligns SnapshotRuntime consent gating with the live SDK.
  • Carries forward the SSR/no-JS behavior needed by the Next.js App Router and Pages Router reference apps.

Benefits to consumers

  • Less integration boilerplate for App Router and Pages Router consumers.
  • One app-local import surface works across App Router server and client components.
  • Pages Router gets a single helper for request binding, state handoff, cookie persistence, and initial page-event deduplication.
  • Server-to-browser state handoff is harder to forget or duplicate.
  • Entry rendering, interaction tracking, and merge-tag rendering use the same OptimizedEntry render-prop shape across server and browser rendering.
  • Manual APIs remain available when an app needs direct request SDK control.

Benefits to maintainers

  • The recommended Next.js integration path is split by actual Next.js router model instead of by the older SSR/Hybrid examples.
  • Server/client boundary behavior is easier to test because the bound factories own the contract.
  • The isomorphic runtime boundary from ✨ feat(react-web): Isomorphic OptimizationProvider for SSR (NT-3560) #354 gives React Web one provider model for SSR first paint and client takeover.
  • Docs can explain primary App Router and Pages Router paths, with lower-level APIs documented as escape hatches.
  • Reference implementations now exercise the preferred public APIs directly.
  • CI filters, root scripts, and E2E docs now match the implementation names that reviewers and consumers see.

Reference implementation rendering improvements

  • Replace nextjs-sdk_ssr and nextjs-sdk_hybrid with nextjs-sdk_pages-router and nextjs-sdk_app-router.
  • Add App Router wiring through an app-local createNextjsAppRouterOptimization() module, exported proxy, layout-level OptimizationRoot, and NextAppAutoPageTracker.
  • Add Pages Router wiring through an app-local createNextjsPagesRouterOptimization() module, a server-only getServerSideOptimizationProps() helper, and _app.tsx state handoff.
  • Extract shared EntryCardContent rendering so server and client entry cards use the same rich text, nested-entry, click-target, and test-attribute behavior.
  • Pass getMergeTagValue from OptimizedEntry render props into rich text render options, aligning merge-tag rendering with the resolved entry/profile context.
  • Run the shared browser E2E suite for CSR, hydration, SSR, server-resolved variants, and JavaScript-disabled SSR behavior in both Next.js router implementations.

Breaking change

NextjsOptimizationState is removed from the client entrypoint. Pass server optimization data through OptimizationRoot or OptimizationProvider via serverOptimizationState, use createNextjsAppRouterOptimization() to let the App Router bound root/provider handle server-to-browser state handoff automatically, or use createNextjsPagesRouterOptimization() with getServerSideOptimizationProps() to pass Pages Router state through pageProps.

[NT-3560]

@phobetron Charles Hudson (phobetron) marked this pull request as draft July 3, 2026 13:13
@phobetron Charles Hudson (phobetron) force-pushed the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch 7 times, most recently from 1575048 to 747554c Compare July 6, 2026 04:52
@phobetron Charles Hudson (phobetron) changed the title 🔥 feat(nexts): Introducing bound Optimization components 🔥 feat(nextjs): Introducing bound Optimization components Jul 6, 2026
@phobetron Charles Hudson (phobetron) marked this pull request as ready for review July 6, 2026 05:00
@phobetron Charles Hudson (phobetron) force-pushed the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch 3 times, most recently from c09fe50 to 41dc1a7 Compare July 6, 2026 12:55
@wiz-inc-38d59fb8d7

wiz-inc-38d59fb8d7 Bot commented Jul 6, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 1 Medium
Software Management Finding Software Management Findings -
Total 1 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

@phobetron Charles Hudson (phobetron) force-pushed the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch 2 times, most recently from 3a811d6 to 5e13147 Compare July 6, 2026 16:02
@phobetron Charles Hudson (phobetron) force-pushed the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch 3 times, most recently from bfe1579 to 483d0af Compare July 6, 2026 17:00
@phobetron Charles Hudson (phobetron) changed the title 🔥 feat(nextjs): Introducing bound Optimization components 🔥 feat(nextjs): Bound Optimization components & Isomorphic Optimization Provider Jul 6, 2026
Introduce router-specific bound Next.js SDK integration surfaces for App Router and Pages Router. Apps bind Optimization SDK config once in an app-local module, then use returned roots, providers, optimized entries, route trackers, proxies, and server helpers across server/client rendering boundaries.

Original to this branch:
- Add the App Router, Pages Router, and Pages Router server entrypoints for bound Next.js integrations while keeping /client, /server, /esr, /request-handler, /tracking-attributes, and /api-schemas as lower-level escape hatches.
- Add automatic App Router server/client components that read request consent, cookies, headers, server Optimization data, entry variants, merge tags, tracking attributes, and state handoff through the bound proxy/root path.
- Add the Pages Router bound client factory and config-bound getServerSideOptimizationProps() helper for getServerSideProps state handoff, response cookie persistence, client consent defaults, and initial page-event deduplication.
- Rename and rebuild the Next.js reference implementations around nextjs-sdk_app-router and nextjs-sdk_pages-router, replacing the older SSR/Hybrid examples.
- Update the reference apps to use app-local bound components, shared EntryCardContent rendering, merge-tag-aware rich text, live updates, preview panel wiring, and route tracking.
- Refresh Next.js, React Web, Web SDK, E2E, consent, locale, profile synchronization, interaction tracking, analytics forwarding, and SDK selection docs around the App Router and Pages Router paths.
- Extend React Web OptimizedEntry render props with OptimizedEntryRenderContext/getMergeTagValue and expose runtime/presentation helpers needed by SSR handoff and tracking.
- Update package exports, build output declarations, bundle-size budgets, CI path filters, root scripts, unit tests, and browser E2E coverage for the new router names and server-resolved variant checks.

Adopted from Tim's `feat/ssr-isomorphic-provider` work (#354):
- Add the isomorphic OptimizationRuntime/SnapshotRuntime and runtime subpaths for core/web packages.
- Make OptimizationProvider/OptimizationRoot render during SSR from serverOptimizationState, then hydrate into the live browser SDK.
- Replace the old NextjsOptimizationState handoff path with provider/root serverOptimizationState handoff.
- Fix SSR variant rendering by priming snapshot/current optimization state before client effects run.
- Preserve injected-SDK first paint, align SnapshotRuntime consent gating with the live SDK, and keep SSR page/variant behavior working without browser JS.

BREAKING CHANGE: NextjsOptimizationState is removed from the client entrypoint. Pass server optimization data through OptimizationRoot or OptimizationProvider via serverOptimizationState, use createNextjsAppRouterOptimization() for App Router handoff, or use createNextjsPagesRouterOptimization() with getServerSideOptimizationProps() for Pages Router handoff.

[[NT-3560](https://contentful.atlassian.net/browse/NT-3560)]
@phobetron Charles Hudson (phobetron) force-pushed the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch from 483d0af to 718b6d9 Compare July 6, 2026 17:20
@phobetron Charles Hudson (phobetron) merged commit 8c4f48e into main Jul 6, 2026
37 checks passed
@phobetron Charles Hudson (phobetron) deleted the NT-3560_nextjs-ssr-hybrid-metaframework-fix branch July 6, 2026 17:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant